/*
 * Copyright (c) 2021 HPMicro
 *
 * SPDX-License-Identifier: BSD-3-Clause
 *
 */

#include "board.h"
#include "hpm_debug_console.h"
#include "hpm_spi_drv.h"
#include "hpm_clock_drv.h"
#include "hpm_gpio_drv.h"
#include "hpm_gpio_drv.h"
#include "hpm_gpiom_drv.h"

#include "jedec_nand_driver.h"


uint8_t test_data[2048] = {0};
uint8_t read_data[2048] = {0};
uint8_t which_board_using = 0;/* 0使用客户板子，1使用6750evk黑板 */
void test_data_make(void)
{
    memset(&test_data[0*256], 0x01,256);
    memset(&test_data[1*256], 0x02,256);
    memset(&test_data[2*256], 0x04,256);
    memset(&test_data[3*256], 0x08,256);
    memset(&test_data[4*256], 0x10,256);
    memset(&test_data[5*256], 0x20,256);
    memset(&test_data[6*256], 0x40,256);
    memset(&test_data[7*256], 0x80,256);
}

uint16_t read_nand_id(SPI_Type* base);
uint32_t nand_flash_write(SPI_Type* base,uint16_t block,uint16_t page,uint16_t col,uint8_t* buf,uint16_t len);
uint32_t nand_flash_read(SPI_Type* base,uint16_t block,uint16_t page,uint16_t col,uint8_t* buf,uint16_t len);
uint32_t flash_Erase(SPI_Type* base,uint16_t block);
void nand_flash_init(SPI_Type* base);

void cs_contorl(uint8_t level){
    if(which_board_using == 1)
    {
        HPM_IOC->PAD[IOC_PAD_PE31].FUNC_CTL = IOC_PE31_FUNC_CTL_GPIO_E_31;
        gpiom_set_pin_controller(HPM_GPIOM, GPIOM_ASSIGN_GPIOE, 31, gpiom_soc_gpio0);
        gpio_set_pin_output(HPM_GPIO0, GPIO_OE_GPIOE, 31);
        gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOE, 31,level);
        if(level == 1)
        {
            HPM_IOC->PAD[IOC_PAD_PE31].FUNC_CTL = IOC_PE31_FUNC_CTL_SPI2_CSN;
        }
    }else{
        HPM_IOC->PAD[IOC_PAD_PB24].FUNC_CTL = IOC_PB24_FUNC_CTL_GPIO_B_24;
        gpiom_set_pin_controller(HPM_GPIOM, GPIOM_ASSIGN_GPIOE, 24, gpiom_soc_gpio0);
        gpio_set_pin_output(HPM_GPIO0, GPIO_OE_GPIOB, 24);
        gpio_write_pin(HPM_GPIO0, GPIO_DO_GPIOB, 24,level);
        if(level == 1)
        {
            HPM_IOC->PAD[IOC_PAD_PB24].FUNC_CTL = IOC_PB24_FUNC_CTL_SPI2_CSN;
        }
    }
    
}

void cus_init_spi_pin(void)
{
    if (which_board_using == 1) {
        HPM_IOC->PAD[IOC_PAD_PE31].FUNC_CTL = IOC_PE31_FUNC_CTL_SPI2_CSN;
        HPM_IOC->PAD[IOC_PAD_PE30].FUNC_CTL = IOC_PE30_FUNC_CTL_SPI2_MOSI;
        HPM_IOC->PAD[IOC_PAD_PE28].FUNC_CTL = IOC_PE28_FUNC_CTL_SPI2_MISO;
        HPM_IOC->PAD[IOC_PAD_PE27].FUNC_CTL = IOC_PE27_FUNC_CTL_SPI2_SCLK | IOC_PAD_FUNC_CTL_LOOP_BACK_SET(1);
        printf("evk pin init ok\r\n");
    }else{
        HPM_IOC->PAD[IOC_PAD_PB24].FUNC_CTL = IOC_PB24_FUNC_CTL_SPI2_CSN;
        HPM_IOC->PAD[IOC_PAD_PB23].FUNC_CTL = IOC_PB23_FUNC_CTL_SPI2_DAT2;
        HPM_IOC->PAD[IOC_PAD_PB20].FUNC_CTL = IOC_PB20_FUNC_CTL_SPI2_DAT3;
        HPM_IOC->PAD[IOC_PAD_PB25].FUNC_CTL = IOC_PB25_FUNC_CTL_SPI2_MISO;
        HPM_IOC->PAD[IOC_PAD_PB22].FUNC_CTL = IOC_PB22_FUNC_CTL_SPI2_MOSI;
        HPM_IOC->PAD[IOC_PAD_PB21].FUNC_CTL = IOC_PB21_FUNC_CTL_SPI2_SCLK | IOC_PAD_FUNC_CTL_LOOP_BACK_MASK;
        HPM_IOC->PAD[IOC_PAD_PE26].FUNC_CTL = IOC_PE26_FUNC_CTL_SPI2_DAT3;
        HPM_IOC->PAD[IOC_PAD_PE29].FUNC_CTL = IOC_PE29_FUNC_CTL_SPI2_DAT2;
        printf("xuchang pin init ok\r\n");
    }
}
uint32_t Spi_Transmit(SPI_Type* base,uint8_t* write_buf,uint8_t* read_buf,uint32_t write_count,uint32_t read_count,uint8_t dummy_count);
void SPI_transfer_function(uint8_t* write_buf,uint8_t* read_buf,uint32_t write_count,uint32_t read_count,uint8_t dummy_count)
{
    cs_contorl(0);
    if(write_count>256)
    {
        for(uint16_t i = 0;i<write_count/256;i++)
        {
            Spi_Transmit(BOARD_APP_SPI_BASE,&write_buf[i*256],read_buf,256,0,0);
        }
        if((write_count%256) == 0){
            
        }else{
            Spi_Transmit(BOARD_APP_SPI_BASE, &write_buf[write_count-(write_count%256)], read_buf, write_count%256,read_count,dummy_count);
        }
    }else if (read_count>256) {
        Spi_Transmit(BOARD_APP_SPI_BASE, write_buf,NULL, write_count, 0,dummy_count);
        for(uint16_t i = 0;i<read_count/256;i++)
        {
            Spi_Transmit(BOARD_APP_SPI_BASE, NULL, &read_buf[i*256],0,256,0);
        }
        if((read_count%256)==0)
        {
        
        }else{
            Spi_Transmit(BOARD_APP_SPI_BASE,NULL,&read_buf[read_count - (read_count%256)],0,read_count%256,0);
        }
        
    }else{
        Spi_Transmit(BOARD_APP_SPI_BASE,write_buf,read_buf,write_count,read_count,dummy_count);
    }
    cs_contorl(1);
}
int main(void)
{
    uint32_t page_num = 0;
    uint16_t block_num = 0;
    char x,y;
    spi_timing_config_t timing_config = {0};
    spi_format_config_t format_config = {0};
    spi_control_config_t control_config = {0};
    hpm_stat_t stat;
    uint32_t spi_clcok;

    /* bsp initialization */
    board_init();
    spi_clcok = board_init_spi_clock(BOARD_APP_SPI_BASE);
    
    printf("which board do you using?\r\n");
    printf("press 0 using xuchang_board , press 1 using hpm_evk_board\r\n");
    which_board_using = getchar() - 48;
    cus_init_spi_pin();
    
    printf("SPI-Master Polling Transfer Example\n");

    /* set SPI sclk frequency for master */
    spi_master_get_default_timing_config(&timing_config);
    timing_config.master_config.clk_src_freq_in_hz = spi_clcok;
    timing_config.master_config.sclk_freq_in_hz = BOARD_APP_SPI_SCLK_FREQ;
    if (status_success != spi_master_timing_init(BOARD_APP_SPI_BASE, &timing_config)) {
        printf("SPI master timming init failed\n");
        while (1) {
        }
    }
    printf("SPI-Master1 transfer timing is configured.\n");
    printf("SPI-Master transfer source clock frequency: %dHz\n", timing_config.master_config.clk_src_freq_in_hz);
    printf("SPI-Master transfer sclk frequency: %dHz\n", timing_config.master_config.sclk_freq_in_hz);

    /* set SPI format config for master */
    spi_master_get_default_format_config(&format_config);
    format_config.common_config.data_len_in_bits = 8;/* 8 bit */
    format_config.common_config.mode = spi_master_mode;
    format_config.common_config.cpol = spi_sclk_high_idle;
    format_config.common_config.cpha = spi_sclk_sampling_even_clk_edges;
    spi_format_init(BOARD_APP_SPI_BASE, &format_config);
    printf("SPI-Master transfer format is configured.\n");

    /* set SPI control config for master */
    spi_master_get_default_control_config(&control_config);
    control_config.master_config.cmd_enable = false;  /* cmd phase control for master */
    control_config.master_config.addr_enable = false; /* address phase control for master */
    control_config.common_config.trans_mode = spi_trans_write_dummy_read;
    control_config.common_config.dummy_cnt = spi_dummy_count_1;

    test_data_make();
    
    //nand_flash_init(BOARD_APP_SPI_BASE);

    init_jedec_nand_handle(&nand_handle,SPI_transfer_function);
    while(1){}
    printf("nand flash test begin to block 0\r\n");
    block_num = 0;/* 你想要测试的block */
    while(1){
        
        printf("now testing block %d\r\n",block_num);
        flash_Erase(BOARD_APP_SPI_BASE,block_num);
        
        for(uint16_t i = 0;i<64;i++)
        {
            nand_flash_write(BOARD_APP_SPI_BASE,block_num,i,0,test_data,2048);
            nand_flash_read(BOARD_APP_SPI_BASE,block_num,i,0,read_data,2048);
            if(memcmp(test_data, read_data, sizeof(test_data)))
            {
                printf("page %d test fail \r\n",i);
            }else{
#if 0
                for(uint32_t i = 0;i<2048;i++)
                {
                    printf("%d ",read_data[i]);
                }
#endif
                printf("page %d test success \r\n",i);
            }
        }
        printf("if you want to test next block please press any key\r\n");
        getchar();
        block_num++;/* 你想要测试的block */
    }

    return 0;
}

uint32_t Spi_Transmit(SPI_Type* base,uint8_t* write_buf,uint8_t* read_buf,uint32_t write_count,uint32_t read_count,uint8_t dummy_count)
{
    hpm_stat_t stat;
    spi_control_config_t control_config = {0};
    /* set SPI control config for master */
    spi_master_get_default_control_config(&control_config);
    control_config.master_config.cmd_enable = false;  /* cmd phase control for master */
    control_config.master_config.addr_enable = false; /* address phase control for master */
    
    switch (dummy_count) {
        case 0:
            control_config.common_config.trans_mode = spi_trans_write_read;
            break;
        case 1:
            control_config.common_config.trans_mode = spi_trans_write_dummy_read;
            control_config.common_config.dummy_cnt = dummy_count -1 ;//spi_dummy_count_1 = 0
            break;
        case 2:
            control_config.common_config.trans_mode = spi_trans_write_dummy_read;
            control_config.common_config.dummy_cnt = dummy_count -1 ;//spi_dummy_count_1 = 0
            break;
        case 3:
            control_config.common_config.trans_mode = spi_trans_write_dummy_read;
            control_config.common_config.dummy_cnt = dummy_count -1 ;//spi_dummy_count_1 = 0
            break;
        default:
            printf("dummy too much\r\n");
            return status_invalid_argument;
        break;
    }
    if(read_count == 0)
    {
        control_config.common_config.trans_mode = spi_trans_write_only;
    }
    if(write_count == 0)
    {
        control_config.common_config.trans_mode = spi_trans_read_only;
    }
    stat = spi_transfer(base,
                &control_config,
                NULL, NULL,
                (uint8_t *)write_buf, write_count, (uint8_t *)read_buf,read_count);
    return stat;
}

uint8_t Get_Nand_Status(SPI_Type* base,uint8_t status_addr)
{
    uint8_t cmd[2] = {0x0f,status_addr};
    uint8_t recive_data = 0;
    Spi_Transmit(base, cmd, &recive_data,2,1,0);
    return recive_data;
}

/**
 * @brief 
 */
uint32_t nand_flash_write(SPI_Type* base,uint16_t block,uint16_t page,uint16_t col,uint8_t* buf,uint16_t len)
{
    uint8_t write_enable = 0x06;
    uint8_t write_cmd[3] = {0};
    uint8_t program_execute_cmd[4] = {0};

    uint16_t program_times = 0;

    uint8_t status = 0;

    uint32_t program_execute = (block << 6) |(page&0x3f);

    write_cmd[0] = 0x02;
    write_cmd[1] = (col & 0x0F00) >> 8;
    write_cmd[2] = col & 0xFF;
    
    Spi_Transmit(base, &write_enable, NULL,sizeof(write_enable),0,0);/* 发06 写使能 */

    cs_contorl(0);/* 这边需要控一下CS，让他符合时序要求，先楫spi单次只能发送最多512字节 */
    Spi_Transmit(base, write_cmd, NULL,sizeof(write_cmd),0,0);/* 发02*/
    for(uint32_t i = 0;i<2048/256;i++)
    {
        Spi_Transmit(base, &buf[i*256], NULL,256,0,0);/* 发02的同时，把数据写入cache */
    }
    cs_contorl(1);
    
    
    program_execute_cmd[0] = 0x10;				/* program execute 0x10 */
    program_execute_cmd[1] = (program_execute & 0xFF0000) >> 16;
    program_execute_cmd[2] = (program_execute & 0xFF00) >> 8;
    program_execute_cmd[3] = program_execute & 0xFF;
    
    Spi_Transmit(base, program_execute_cmd, NULL,sizeof(program_execute_cmd),0,0);/* 发10 将缓冲区的数据写入nand */
    
    while(1){
        status = Get_Nand_Status(base,0xc0);/* 查看写到缓冲区的内容是否成功*/
        if((status&0x04) == 0)
        {
            return status_success;
        }
        program_times++;
        if(program_times >500)
        {
            break;
        }
    }
    return status_fail;
}

uint32_t nand_flash_read(SPI_Type* base,uint16_t block,uint16_t page,uint16_t col,uint8_t* buf,uint16_t len)
{
    uint32_t ui32Addr = (block << 6) + page;	
    uint8_t read_page_to_cache[4] = {0};
    uint8_t read_from_cache[3] = {0};
    uint8_t status = 0;
    uint16_t loop_times = 0;
    read_page_to_cache[0] = 0x13;
    read_page_to_cache[1] = (uint8_t)(ui32Addr >> 16);
    read_page_to_cache[2] = (uint8_t)(ui32Addr >> 8);
    read_page_to_cache[3] = (uint8_t)ui32Addr;


    Spi_Transmit(base, read_page_to_cache, NULL,sizeof(read_page_to_cache),0,0);/* 命令nand将该page的数据写入cache */

    while(1){
        status = Get_Nand_Status(base,0xc0);/* 查看写到缓冲区的内容是否正确 */
        if((status&0x01) == 0)
        {
            break;
        }
        loop_times++;
        if(loop_times >500)
        {
            printf("Nand read page to cache failed\r\n");
            return status_fail;
        }
    }
    for(uint16_t i = 0;i<2048/256;i++)
    {  
         read_from_cache[0] = 0x03;
         read_from_cache[1] = (col & 0xFF00)>>8;
         read_from_cache[2] = col&0xff;
         col = col+256;
         Spi_Transmit(base, read_from_cache, &buf[i*256],sizeof(read_from_cache),256,1);/* 命令nand将该page的数据写入cache */
    }
}

uint16_t read_nand_id(SPI_Type* base)
{
    uint8_t wbuff[1] = {0x9f};
    uint16_t rbuff = {0};

    Spi_Transmit(base, wbuff, (uint8_t*)&rbuff,sizeof(wbuff),2,1);
    return rbuff;
}

void nand_flash_init(SPI_Type* base)
{
    uint8_t reset_cmd = 0xff;
    uint16_t nand_id = 0;
    uint8_t ui8Feature = 0 			|
					//	0x01 << 7	|	// OTP_PRT
					//	0x01 << 6	|	// OTP_EN
						//0x01 << 4	|	// ECC_EN
					//	0x01 << 0	|	// QE,quad IO operations can be executed
						0x00;
    uint8_t status = 0;
    uint8_t block_protection_unlock[3] = {0x1f,0xa0,0x00};
    uint8_t block_ecc_en[3] = {0x1f,0xb0,ui8Feature};
    Spi_Transmit(base, &reset_cmd, NULL,sizeof(reset_cmd),0,0);/* 复位nand */
    nand_id = read_nand_id(base);/*读nand id*/
    printf("nand id = 0x%x\r\n",nand_id);

    Spi_Transmit(base, block_protection_unlock, NULL,sizeof(block_protection_unlock),0,0);/* 解除保护 */

    status = Get_Nand_Status(base,0xc0);/*  */
    printf("address C0 value is=%x\r\n",status);

    Spi_Transmit(base, block_ecc_en, NULL,sizeof(block_ecc_en),0,0);/* 解除保护 */

    status = Get_Nand_Status(base,0xb0);/*  */
    printf("address B0 value is=%x\r\n",status);
}

uint32_t flash_Erase(SPI_Type* base,uint16_t block)
{
	uint8_t test_time=0;
        uint8_t write_enable = 0x06;
        Spi_Transmit(base, &write_enable, NULL,sizeof(write_enable),0,0);/* 发06 写使能 */
	
	uint32_t ui32Addr = block << 6;
	uint8_t ui8aCmd[4] = {0xD8,(ui32Addr >> 16) & 0xFF,(ui32Addr >> 8) & 0xFF,ui32Addr & 0xFF};

        Spi_Transmit(base, ui8aCmd, NULL,sizeof(ui8aCmd),0,0);/* 发d8 发需要擦除的block */
	
	volatile uint8_t ui8Status;

	while (1)
	{
		ui8Status=Get_Nand_Status(base,0xC0);
		if (0x00 == (ui8Status & 0x01)) break;		// 擦写完成？
                board_delay_ms(1);
                test_time++;
		if (test_time > 50)
		{
			return status_fail;
		}
	}
	return status_fail;

}